import json
import asyncio
from io import BytesIO
from pathlib import Path

import httpx
from PIL import Image
from nonebot import logger

from .config import genshin_resource_config

IMG_PATH = Path(__file__).absolute().parent.parent / "img"
DATA_PATH = Path(genshin_resource_config.genshin_resource_data_path)

map_list_url = genshin_resource_config.map_list_url
map_info_url = genshin_resource_config.map_info_url
icon_list_url = genshin_resource_config.icon_list_url


async def download_url(url: str) -> httpx.Response:
    async with httpx.AsyncClient() as client:
        for i in range(3):
            try:
                response = await client.get(url, timeout=20)
                response.raise_for_status()
                return response
            except Exception as e:
                logger.warning(f"下载出错{url},第{i}次重试: {e}")
                await asyncio.sleep(3)
    raise Exception(f"{url} 下载失败！")


async def download_map(map_id: str):
    map_info = (await download_url(map_info_url % map_id)).json()["data"]["info"]
    map_detail = json.loads(map_info["detail"])
    map_url_list = map_detail['slices']
    total_x, total_y = map_detail["total_size"]

    raw_map: Image = Image.new("RGB", (total_x, total_y))
    x_offset = y_offset = 0
    for map_url in map_url_list:
        done = await asyncio.gather(*map(lambda x: asyncio.create_task(download_url(x["url"])), map_url))
        _y_offset = 0
        for i in done:
            part = Image.open(BytesIO(i.content))
            raw_map.paste(part, (x_offset, y_offset))
            x_offset += part.size[0]
            _y_offset = part.size[1]
        x_offset = 0
        y_offset += _y_offset

    with open(DATA_PATH / "map" / f"{map_id}.png", "wb") as map_file:
        raw_map.save(map_file)


async def download_all_map(forcibly=False):
    logger.info("下载地图文件")

    map_dir = DATA_PATH / "map"
    if not map_dir.exists():
        map_dir.mkdir(parents=True)

    data = (await download_url(map_list_url)).json()
    for map_id in [str(i["id"]) for i in data["data"]["list"]]:
        if not (map_dir / f"{map_id}.png").exists() or forcibly:
            await download_map(map_id)

    logger.info("下载地图文件完成")


async def download_icon(icon_info):
    box = Image.open(IMG_PATH / "icon.png").convert('RGBA')
    box_alpha = Image.open(IMG_PATH / "icon-alpha.png").crop((20, 8, 105, 93)).getchannel("A")
    icon = Image.open(BytesIO((await download_url(icon_info["icon"])).content)).resize((85, 85))
    new_icon = Image.new("RGBA", (85, 85))
    new_icon.paste(icon, None, box_alpha)
    box.paste(new_icon, (20, 8, 105, 93), new_icon)
    with open(DATA_PATH / "icon" / f"{icon_info['id']}.png", "wb") as icon_file:
        box.save(icon_file)


async def download_all_icon(forcibly=False):
    logger.info("下载图标文件")

    icon_dir = DATA_PATH / "icon"
    if not icon_dir.exists():
        icon_dir.mkdir(parents=True)

    data = (await download_url(icon_list_url)).json()
    tasks = []
    for group in data["data"]["tree"]:
        for icon in group["children"]:
            if not (icon_dir / f"{icon['id']}.png").exists() or forcibly:
                tasks.append(asyncio.create_task(download_icon(icon)))
    await asyncio.gather(*tasks)
    logger.info("下载图标文件完成")


async def download_transport(transport_info):
    icon = Image.open(BytesIO((await download_url(transport_info["icon"])).content)).resize((100, 100))
    with open(DATA_PATH / "transport" / f"{transport_info['id']}.png", "wb") as icon_file:
        icon.save(icon_file)


async def download_all_transport(forcibly=False):
    logger.info("下载传送点")

    transport_dir = DATA_PATH / "transport"
    if not transport_dir.exists():
        transport_dir.mkdir(parents=True)

    data = (await download_url(icon_list_url)).json()
    tasks = []
    for icon in data["data"]["tree"][0]["children"]:
        if not (transport_dir / f"{icon['id']}.png").exists() or forcibly:
            tasks.append(asyncio.create_task(download_transport(icon)))
    await asyncio.gather(*tasks)
    logger.info("下载传送点完成")


async def download_resources(forcibly=False):
    await asyncio.gather(download_all_icon(forcibly), download_all_map(forcibly), download_all_transport(forcibly))
